#ifdef _WIN32
#include "getSystemInfo.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <iphlpapi.h>
#include <lm.h>
#include "../Publicfunc/publicFunc.h"

#pragma comment(lib, "Netapi32.lib")
#pragma comment(lib, "Iphlpapi.lib")

constexpr int NAME_MAX_LEN = 128;

#define WORKING_BUFFER_SIZE 15000
#define MAX_TRIES 3
void getSystemInfo::getMacAndIPs()
{
	bool findedMac = false;
	DWORD dwSize = 0;
	DWORD dwRetVal = 0;
	unsigned int i = 0;
	ULONG flags = GAA_FLAG_INCLUDE_PREFIX;
	ULONG family = AF_UNSPEC;

	PIP_ADAPTER_ADDRESSES pAddresses = NULL;
	ULONG outBufLen = 0;
	ULONG Iterations = 0;

	PIP_ADAPTER_ADDRESSES pCurrAddresses = NULL;
	PIP_ADAPTER_UNICAST_ADDRESS pUnicast = NULL;

	family = AF_INET;

	outBufLen = WORKING_BUFFER_SIZE;

	do {

		pAddresses = (IP_ADAPTER_ADDRESSES*)malloc(outBufLen);
		if (pAddresses == NULL) {
			exit(1);
		}

		dwRetVal = GetAdaptersAddresses(family, flags, NULL, pAddresses, &outBufLen);

		if (dwRetVal == ERROR_BUFFER_OVERFLOW) {
			free(pAddresses);
			pAddresses = NULL;
		}
		else {
			break;
		}

		Iterations++;

	} while ((dwRetVal == ERROR_BUFFER_OVERFLOW) && (Iterations < MAX_TRIES));

	if (dwRetVal == NO_ERROR) {
		i = 0;
		pCurrAddresses = pAddresses;
		while (pCurrAddresses) {
			findedMac = false;
			if (pCurrAddresses->PhysicalAddressLength != 0) {
				mac.at(i) = *(int64_t*)pCurrAddresses->PhysicalAddress;

				char lvMacstr[32] = { 0 };
				SC::int64_to_mac_string(mac.at(i), lvMacstr, sizeof(lvMacstr));
				macAddr[i] = std::string(lvMacstr);

				findedMac = true;
			}

			if (findedMac)
			{
				pUnicast = pCurrAddresses->FirstUnicastAddress;
				if (pUnicast)
				{
					if (AF_INET == pUnicast->Address.lpSockaddr->sa_family)// IPV4 
					{
						
#ifdef LOWERWIN8
						char *tmpIP = inet_ntoa(((struct sockaddr_in*)pUnicast->Address.lpSockaddr)->sin_addr);
#else
						char tmpIP[32] = { 0 };
						inet_ntop(PF_INET, &((struct sockaddr_in*)pUnicast->Address.lpSockaddr)->sin_addr, tmpIP, sizeof(tmpIP));
#endif
						localIP.at(i) = std::string(tmpIP);
					}
				}
				i++;
			}
			pCurrAddresses = pCurrAddresses->Next;
		}
	}
	else {
		;
	}

	if (pAddresses) {
		free(pAddresses);
	}
}

void getSystemInfo::getDoMain()
{
	NET_API_STATUS status;
	LPWSTR lpwszDomainName = NULL;
	NETSETUP_JOIN_STATUS joinStatus;
	status = NetGetJoinInformation(NULL, &lpwszDomainName, &joinStatus);
	if (status == NERR_Success) {
		switch (joinStatus) {
		case NetSetupDomainName:
		{
			WCHAR domainName[MAX_PATH];
			DWORD size = MAX_PATH;
			if (GetComputerNameExW(ComputerNameDnsDomain, domainName, &size)) {
				char* lvDomain = SC::wChar2Char(domainName, false);
				domain = std::string(lvDomain);
				free(lvDomain);
			}
			break;
		}
		default:
			domain = std::string("N/A");
			break;
		}
		NetApiBufferFree(lpwszDomainName);
	}
	else {
		domain = std::string("N/A");
	}
}

typedef NTSTATUS(WINAPI* RtlGetVersion_PTR)(PRTL_OSVERSIONINFOEXW);
void getSystemInfo::getWindowsVersion()
{
	HMODULE h_NtDll = GetModuleHandleW(L"ntdll.dll");
	RtlGetVersion_PTR fnRtlGetVersion = (RtlGetVersion_PTR)GetProcAddress(h_NtDll, "RtlGetVersion");

	if (NULL != fnRtlGetVersion)
	{
		RTL_OSVERSIONINFOEXW rovi = { 0 };
		rovi.dwOSVersionInfoSize = sizeof(rovi);
		if (0 == fnRtlGetVersion(&rovi))
		{
			char version[32] = {};
			sprintf_s(version, sizeof(version), "%d.%d.%d", rovi.dwMajorVersion, rovi.dwMinorVersion, rovi.dwBuildNumber);
			windowsVersion = std::string(version);
		}
	}
}

void getSystemInfo::getStartupPath()
{
	wchar_t wtmpPath[MAX_PATH] = { 0 };
	GetModuleFileNameW(NULL, wtmpPath, MAX_PATH);
	char* tmpPath = SC::wChar2Char(wtmpPath, false);
	startupPath = std::string(tmpPath);
	free(tmpPath);
}

void getSystemInfo::getInfo()
{
	getMacAndIPs();
	getDoMain();
	getWindowsVersion();
	unsigned long lvReaded = NAME_MAX_LEN;
	std::array<char, NAME_MAX_LEN> lvName;
	bool ret = GetComputerNameA(lvName.data(), &lvReaded);
	if (ret)
	{
		this->computerName = std::string(lvName.data());
	}

	lvName.fill('\0');
	lvReaded = NAME_MAX_LEN;
	ret = GetUserNameA(lvName.data(), &lvReaded);
	if (ret)
	{
		this->userName = std::string(lvName.data());
	}
}
#endif